/**
 * Mapped widget - visualize html image maps. Contains "mapped" and "area" 
 * jquery widgets extensions   
 */
(function( $, undefined ) {
/**
 * Mapped widget - searches for html image maps, processes them and contains
 * main behaviour logic implementation  
 */
$.widget("ui.mapped", {
    /**
     * Widget options
     */
	options : {
        className: 'mapped',  // class name of the area element
        areaResize: null,     // resize callback, user defined function called after resize action
        areaDrop: null,       // drop callback, user defined function called after area move action
        areaResizable: false, // identifies if area should be resizable or not
        areaDraggable: false, // identifies if area should be draggable or not
        areaText: 'href',     // areas text, possible params: [href|coords|none]
        container: 'self',    // areas container, possible params: [self|parent]
        visible: false
    },

    /**
     * Method maps all child images at given element
     */
    _create : function() {
    	var self   = this;
    	var selfEl = this.element;
    	var cont   = (this.options.container == 'self') ? selfEl : selfEl.parent();
    	// Finding all images at given element
    	$('img', selfEl).each(function(){
    		var img = $(this);
    		// if image mapped, creating area object by given html map element  
    		if (img.attr('usemap')) {
    			selfEl.append(self._createImgHelper($(this)));
    			$(img.attr('usemap'), selfEl).children().each(function() {
    				selfEl.append(self._createArea(img, cont, $(this)));
    			});
    		// otherwise mapping whole image in case of his container is <a> tag 
    		} /* else if (img.parent().prop("tagName") == 'A') {
    			selfEl.append(self._createArea(img, cont, null));	
    		}*/
    	});
    },
    
    /**
     * Method creates area objects with given options
     * 
     * @param parent    - area parent image
     * @param container - area container element
     * @param area      - area object itself with coords and href attribs
     * 
     * @return hidden div element with area properties 
     */
    _createArea : function(parent, container, area) {
		var o = this.options;
    	overlayEl = $('<div>')
			.addClass(this.options.className + (this.element.attr('id') ? ' ' + this.element.attr('id') : ''))
			.area({
				parentImg: parent,
				container: container,
				area: area,
				resizable: o.areaResizable,
				draggable: o.areaDraggable,
				resize: o.areaResize,
				drop: o.areaDrop,
				text: o.areaText
			});
		return overlayEl;
    },
    
    _createImgHelper : function(img) {
    	var addButton = $('<div>')
    		.addClass('img-helper ui-corner-all ui-icon ui-icon-circle-plus')
    		.data('pp', img.position());
    	return addButton;
    },
    
    /**
     * Method refreshes all area elements, recalculates position etc...
     */
    refresh : function() {
    	this.element.parent().scrollTop(0);
    	$('.' + this.options.className, this.element).each(function() {
    		$(this).area('refresh');
    	});
    	$('.img-helper', this.element).each(function() {
    		$(this).removeAttr('style');
    		$(this).offset({top: $(this).data('pp').top - 16, left: $(this).data('pp').left - 16});
    	});
    },
    
    /**
     * Method shows all area elements
     */
    show : function() {
    	this.refresh();
    	this.hide();
    	$('.' + this.options.className, this.element).show();
    	$('.img-helper', this.element).show();
    	this.options.visible = true;
    },
    
    /**
     * Method hides all area elements
     */
    hide : function() {
    	$('.' + this.options.className, this.element).hide();
    	$('.img-helper', this.element).hide();
    	this.options.visible = false;
    },

    /**
     * Destructor
     */
    destroy: function() {
    	$('.' + this.options.className, this.element).each(function() {
    		$(this).remove();
    	});
        $.Widget.prototype.destroy.apply( this, arguments );
    }
});

$.extend($.ui.mapped, {
    version: "0.1"
});

/**
 * Area widget - creates visual objects that represents position of corresponding
 * html area elemnts. Make them resizable and draggable.   
 */
$.widget("ui.area", {
    /**
     * Widget options 
     */
	options : {
        parentImg: null,  // area correspond image
        area: null,       // area object
        container: null,  // areas container, possible params: [self|parent]
        resizable: false, // identifies if area should be resizable or not
        draggable: false, // identifies if area should be draggable or not
        text: 'href'      // areas text, possible params: [href|coords|none]
    },

    /**
     * Method creates visual area object, makes it resizable/draggable.
     * Sets callback functions. 
     */
    _create : function() {
    	var self = this;
    	if (self.options.area) {
	    	if (self.options.resizable) {
	    		this.element.resizable({
		    		start: function(event, ui){
		    			if (self.options.container.prop("tagName") != 'BODY') {
		    				ui.position.top += self.options.container.scrollTop();
		    			}
		    		},
		    		stop: function(event, ui){
		    			if (ui.size.height > self.options.parentImg.height()) {
		    				$(this).height(ui.size.height = self.options.parentImg.height());
		    			}
		    	
		    			if (ui.size.width > self.options.parentImg.width()) {
		    				$(this).width(ui.size.width = self.options.parentImg.width());
		    			}
		    			
		    			var coords = self.options.area.attr('coords').split(',');
		    			coords[2] = parseInt(coords[2]) + (ui.size.width - ui.originalSize.width);
		    			coords[3] = parseInt(coords[3]) + (ui.size.height - ui.originalSize.height);
		    			self.options.area.attr('coords', coords.join(','));
		    			self._trigger('resize', event);
		    		}
		    	});
	    	}
	    	
	    	if (self.options.draggable) {
	    		this.element.draggable({
		    		containment: this.options.parentImg,
		    		stop: function(event, ui) {
		    			var coords = self.options.area.attr('coords').split(',');
		    			coords[0] = parseInt(coords[0]) + (ui.position.left - ui.originalPosition.left);
		    			coords[1] = parseInt(coords[1]) + (ui.position.top - ui.originalPosition.top);
		    			coords[2] = parseInt(coords[2]) + (ui.position.left - ui.originalPosition.left);
		    			coords[3] = parseInt(coords[3]) + (ui.position.top - ui.originalPosition.top);
		    			self.options.area.attr('coords', coords.join(','));
		    			self._trigger('drop', event);
		    		}
		    	})
		    	.on('click', function(event) {
		    		self._trigger('drop', event);
		    	});
	    	}
	    	
	    	if (this.options.text != 'none') {
	    		this.element.append(
	    			$('<div>').addClass('mapped-text').text(self.options.area.attr(self.options.text))
	    		);
	    	}
    	}
    	this.element.hide();
    },
    
    /**
     * Method refreshes area position
     */
    refresh : function() {
    	selfEl = $(this.element);
    	selfEl.removeAttr('style');
    	if (this.options.draggable) {
    		selfEl.css('cursor', 'move');
    	}
    	
    	var coords   = [];
    	var parentEl = this.options.parentImg;
    	if (this.options.area == null) {
    		coords = [0, 0, parentEl.width(), parentEl.height()];
    	} else {
    		coords = this.options.area.attr('coords').split(',');
    	}
		
		var offsetX = parseInt(coords[0]);
		var offsetY = parseInt(coords[1]);
		
		selfEl.width(coords[2] - coords[0]);
		selfEl.height(coords[3] - coords[1]);
		
		selfEl.position({
			of: parentEl,
			my: "left top",
			at: "left top",
			offset: offsetX + " " + offsetY,
			collision: 'none'
		});
    },
    
    /**
     * Destructor
     */
    destroy: function() {
        $.Widget.prototype.destroy.apply( this, arguments );
    },
});

$.extend($.ui.area, {
    version: "0.1"
});

}(jQuery));